package com.demo.aop;
import com.demo.annotation.Aspectj;
import com.demo.annotation.PointCut;
import com.demo.core.BeanDefinition;
import com.demo.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ProxyFactory {
//    定义的bean
    private Set<BeanDefinition> definitions;

    private Set<ProxyConfig> proxyConfigs; //指的是 真实对象id和 这个对象需要增强额外功能类。
// 帮助构建代理对象
    private ProxyHelper proxyHelper;
    public ProxyFactory(Set<BeanDefinition> definitions) {
        proxyHelper = new ProxyHelper();
        this.definitions =definitions;
        this.proxyConfigs = getProxyDefinitions(definitions);// 获得所有额外功能类 ，此时，额外功能类已经在ioc容器中
    }
//    构建代理对象
    public Object createProxyObject(AopWrapper aopWrapper){
        Object res=proxyHelper.helpCreateProxyObject(aopWrapper);
        return res;
    }



    /**
     * 遍历所有的definitions ，得到带有aop注解的bean定义
     * @param definitionSet
     * @return
     */
    public Set<BeanDefinition> getAllAopDefinitions(Set<BeanDefinition> definitionSet){
        Set<BeanDefinition> aops = new HashSet<>();
        definitionSet.forEach(x->{
            Class beanClass = x.getBeanClass();
            if(beanClass.isAnnotationPresent(Aspectj.class)){ //是aop注解 标志着是一个额外功能类
                aops.add(x);
            }
        });
        return aops;
    }
//    得到带有PointCut 注解的方法 上携带的信息
    public String getPointCutInfo(Method[] methods){
        for (Method m : methods) {
            if(m.isAnnotationPresent(PointCut.class)){
                return  m.getAnnotation(PointCut.class).value();
            }
        }
        return null;
    }
// 得到 把额外功能对象 的 额外方法和切入点的信息封装一下。
    public Set<PointCutWrapper> getPointInfo(Set<BeanDefinition> definitionSet){
        Set<BeanDefinition> aopSet = this.getAllAopDefinitions(definitionSet); //获得所有的额外功能类
        Set<PointCutWrapper> wrappers = new HashSet<>();
        // 从aopSet中得到aop 额外功能类 和切入点的一些信心
        aopSet.forEach(aop->{
            Class aopClass = aop.getBeanClass();
            //得到定义的切入点的值
            String pointCutInfo = getPointCutInfo(aopClass.getDeclaredMethods());
            if(pointCutInfo!=null){
                Integer pointCutLevel = StringUtils.pointCutLevel(pointCutInfo);
                wrappers.add(new PointCutWrapper(pointCutLevel,pointCutInfo,aop.getBeanName()));
            }

        });
        return  wrappers;
    }

    public Set<ProxyConfig> getProxyDefinitions(Set<BeanDefinition> definitionSet){
        Set<PointCutWrapper> pointInfo = getPointInfo(definitionSet);// 里面定义的切入点信息
        Set<ProxyConfig> proxyConfigs = new HashSet<>();
        // 在这里 根据pointCut的信息，把相应的 PointInfo封装进 ProxyConfig中
        definitionSet.forEach(def->{
            if (!def.getClass().isAnnotationPresent(Aspectj.class)){ //直接抛出掉额外功能类
                Class beanClass = def.getBeanClass();
                List<PointCutWrapper> pointCutWrappers=new ArrayList<>();
                // 每一个 额外功能类 都扫描一遍 ，把符合切入点的额外信息类 构造成 一个 配置信息类
                pointInfo.forEach(pinfo->{
                    if(pinfo.getLevel()==1||pinfo.getLevel()==2){ //包和类 额外功能等级
                        if(beanClass.getName().contains(pinfo.getDestination())){
                            pointCutWrappers.add(pinfo);
                        }
                    }
                    if(pinfo.getLevel()==3){ //方法额外功能等级
                        if(pinfo.getDestination().contains(beanClass.getName())){
                                pointCutWrappers.add(pinfo);
                            }
                    }
                });
                proxyConfigs.add(new ProxyConfig(def.getBeanName(),pointCutWrappers));
            }
        });


        return proxyConfigs;
    }

    public Set<ProxyConfig> getProxyConfigs() {
        return proxyConfigs;
    }

    public void setProxyConfigs(Set<ProxyConfig> proxyConfigs) {
        this.proxyConfigs = proxyConfigs;
    }

    public Set<BeanDefinition> getDefinitions() {
        return definitions;
    }

    public void setDefinitions(Set<BeanDefinition> definitions) {
        this.definitions = definitions;
    }

    public ProxyHelper getProxyHelper() {
        return proxyHelper;
    }

    public void setProxyHelper(ProxyHelper proxyHelper) {
        this.proxyHelper = proxyHelper;
    }
}
